import sys
import os
import time
import subprocess
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QSplitter, QTreeView, QTabWidget, QStatusBar, 
    QVBoxLayout, QWidget, QTextEdit, QGraphicsView, QGraphicsScene, QGraphicsRectItem,
    QToolBar, QMessageBox, QPushButton, QLabel, QMenu, 
    QInputDialog, QFileDialog, QDialog, QDialogButtonBox, QSizePolicy
)
from PyQt6.QtGui import (
    QIcon, QTextCharFormat, QColor, QFont, QPen, QBrush, QPainterPath, QFileSystemModel,
    QTextCursor, QPalette, QStandardItemModel, QStandardItem, QSyntaxHighlighter, 
    QRegularExpressionValidator, QAction, QPainter
)
from PyQt6.QtCore import (
    Qt, QRectF, QPointF, QModelIndex, QFileInfo, QSortFilterProxyModel,
    QRegularExpression, QTimer, QSize
)
import git
from git import Repo, GitCommandError, InvalidGitRepositoryError





# ===================== Git 管理器 =====================
class GitManager:
    def __init__(self):
        self.repo: Repo = None
        self.current_branch = "main"
    
    def open_repo(self, path: str) -> (bool, str):
        try:
            self.repo = Repo(path)
            self.current_branch = self.repo.active_branch.name
            return True, "仓库已打开"
        except (InvalidGitRepositoryError, TypeError):
            return False, "无效的Git仓库"
        except Exception as e:
            return False, f"错误: {str(e)}"
    
    def clone_repo(self, url: str, path: str) -> (bool, str):
        try:
            self.repo = Repo.clone_from(url, path)
            self.current_branch = self.repo.active_branch.name
            return True, "仓库已克隆"
        except GitCommandError as e:
            return False, f"克隆失败: {str(e)}"
    
    def commit(self, message: str, file_list=None) -> (bool, str):
        if not self.repo:
            return False, "无活动仓库"
        
        try:
            # 检查是否有变更
            if not file_list and not self.repo.is_dirty():
                return False, "没有变更可提交"
            
            # 添加所有更改为暂存区
            if not file_list:
                self.repo.git.add(A=True)
            else:
                for file_path in file_list:
                    self.repo.git.add(file_path)
            
            # 执行提交
            self.repo.git.commit(m=message)
            return True, f"提交成功: {message}"
        except GitCommandError as e:
            return False, f"提交失败: {str(e)}"
    
    def checkout(self, branch_name: str) -> (bool, str):
        if not self.repo:
            return False, "无活动仓库"
        
        try:
            # 如果分支不存在则创建
            if branch_name not in [b.name for b in self.repo.branches]:
                self.repo.git.branch(branch_name)
            
            # 检出分支
            self.repo.git.checkout(branch_name)
            self.current_branch = branch_name
            return True, f"已切换到分支 {branch_name}"
        except GitCommandError as e:
            return False, f"切换分支失败: {str(e)}"
    
    def get_status(self) -> dict:
        status = {"staged": [], "unstaged": [], "untracked": []}
        if not self.repo:
            return status
        
        try:
            # 检查仓库是否为空（没有提交）
            if not self.repo.head.is_valid():
                # 处理空仓库情况
                status["untracked"] = self.repo.untracked_files
                return status
            
            # 获取未暂存的更改
            changed = [item.a_path for item in self.repo.index.diff(None)]
            
            # 获取未跟踪的文件
            untracked = self.repo.untracked_files
            
            # 获取已暂存的文件 - 使用更安全的方法
            try:
                diff = self.repo.index.diff("HEAD")
                staged = [d.a_path for d in diff]
            except GitCommandError:
                # 处理空仓库情况
                staged = list(self.repo.index.entries.keys())
            
            status = {
                "staged": staged,
                "unstaged": changed,
                "untracked": untracked
            }
            return status
        except Exception as e:
            print(f"获取状态出错: {str(e)}")
            return status
    
    def push(self) -> (bool, str):
        if not self.repo:
            return False, "无活动仓库"
        
        try:
            self.repo.git.push()
            return True, "推送成功"
        except GitCommandError as e:
            return False, f"推送失败: {str(e)}"
    
    def pull(self) -> (bool, str):
        if not self.repo:
            return False, "无活动仓库"
        
        try:
            self.repo.git.pull()
            return True, "拉取成功"
        except GitCommandError as e:
            return False, f"拉取失败: {str(e)}"
    
    def visualize_history(self) -> dict:
        if not self.repo:
            return {"nodes": [], "edges": []}
        

        # 检查仓库是否为空
        if not self.repo.head.is_valid():
            return {"nodes": [], "edges": []}

        # 获取提交历史
        commits = list(self.repo.iter_commits())
        if not commits:
            return {"nodes": [], "edges": []}
        
        # 处理为可视化的节点结构
        graph = {"nodes": [], "edges": []}
        commit_map = {}
        
        for idx, commit in enumerate(commits):
            branch_names = []
            for branch in self.repo.branches:
                if branch.commit == commit:
                    branch_names.append(branch.name)
            
            node = {
                "id": commit.hexsha[:7],
                "message": commit.message.split("\n")[0],
                "author": commit.author.name,
                "date": time.strftime("%Y-%m-%d", time.gmtime(commit.committed_date)),
                "tags": branch_names,
                "is_head": commit == self.repo.head.commit
            }
            
            graph["nodes"].append(node)
            commit_map[commit.hexsha] = node
            
            # 处理父节点关系
            for parent in commit.parents:
                if parent.hexsha in commit_map:
                    graph["edges"].append({
                        "source": parent.hexsha[:7],
                        "target": commit.hexsha[:7],
                        "merge": len(commit.parents) > 1
                    })
        
        return graph
    
    def get_branches(self) -> list:
        if not self.repo:
            return []
        return [branch.name for branch in self.repo.branches]